﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Data;
using System.Data.SqlClient;
using System.Xml;

namespace MyLibrary.IO
{
    /// <summary>
    /// 日志信息的内容
    /// </summary>
    public enum LogType
    {
        /// <summary>
        /// 信息
        /// </summary>
        Info = 0,
        /// <summary>
        /// Bug
        /// </summary>
        Bug = 1,
        /// <summary>
        /// 警告
        /// </summary>
        Warming = 2,
        /// <summary>
        /// 错误
        /// </summary>
        Error = 3,
        /// <summary>
        /// 异常
        /// </summary>
        Exception = 4
    }

    /// <summary>
    /// 日志存放的方式
    /// </summary>
    public enum LogFormat
    {
        /// <summary>
        /// XML模式
        /// </summary>
        XML,
        /// <summary>
        /// 普通文本(默认)
        /// </summary>
        Text,
        /// <summary>
        /// 数据库存放
        /// </summary>
        Database
    }

    /// <summary>
    /// 日志存放的方式,不可继承,不可实例化
    /// </summary>
    public enum LogSaveMode
    {
        /// <summary>
        /// 单一文件(表),不限制增长(默认)
        /// </summary>
        Single,
        /// <summary>
        /// 周期性记录文件
        /// </summary>
        Cycle,
        /// <summary>
        /// 对每个日志文件(表)进行大小限制
        /// </summary>
        Limit
    }

    /// <summary>
    /// 日志超过设定的存放限制后的处理方式
    /// </summary>
    public enum LogStoreMode
    {
        /// <summary>
        /// 不处理
        /// </summary>
        NoDeal,
        /// <summary>
        /// 进行压缩处理(默认)
        /// </summary>
        Compression,
        /// <summary>
        /// 删除
        /// </summary>
        Delete
    }

    /// <summary>
    /// 日志操作类[保持原有调用不变]
    /// </summary>
    /// <remarks>改造计划:
    /// 1.[支持配置文件]
    /// 2.[支持日志表的自动建立]
    /// 3.[支持多种数据库]
    /// </remarks>
    public class Logger
    {
        #region 单件模式
        private Logger() { }
        private static Logger _logger = null;
        private static SqlConnection _connection = null;
        public static Logger CreateLogerInstance()
        {
            if (_logger == null)
                _logger = new Logger();
            return _logger;
        }
        public static Logger CreateLogerInstance(string dbConnStr, string tableName)
        {
            if(String.IsNullOrEmpty(dbConnStr)) throw new ArgumentNullException("连接字符串不能为空");
            if (String.IsNullOrEmpty(tableName)) throw new ArgumentNullException("日志表不能为空");
            if (_logger == null)
            {
                _logger = new Logger();
                _logger._logTable = tableName;
                _connection = new SqlConnection();
                _connection.ConnectionString = dbConnStr;
            }
            return _logger;
        }
        public static void LoggerDispose()
        {
            if (_connection != null)
            {
                if (_connection.State != ConnectionState.Closed)
                    _connection.Close();
                _connection.Dispose();
            }
        }
        #endregion

        private long _filesizelimit = (20 * 1024 * 1024);
        /// <summary>
        /// 日志限制的大小(默认为20M)
        /// </summary>
        public long Filesizelimit
        {
            get { return _filesizelimit; }
            set { _filesizelimit = value; }
        }

        private string _logPath = String.Empty;
        /// <summary>
        /// 日志保存路径
        /// </summary>
        public string LogPath
        {
            get { return _logPath; }
            set { _logPath = value; }
        }

        private LogSaveMode _logsavemode = LogSaveMode.Limit;
        /// <summary>
        /// 日志保存方式(默认:限制大小方式)
        /// </summary>
        protected LogSaveMode Logsavemode
        {
            get { return _logsavemode; }
            set { _logsavemode = value; }
        }

        private LogStoreMode _logstoremode = LogStoreMode.Compression;
        /// <summary>
        /// 日志存放方式(默认:压缩超过大小的日志文件)
        /// </summary>
        protected LogStoreMode Logstoremode
        {
            get { return _logstoremode; }
            set { _logstoremode = value; }
        }

        private LogFormat _logformat = LogFormat.Text;
        /// <summary>
        /// 日志存放方式
        /// </summary>
        public LogFormat LogStoreType
        {
            get { return _logformat; }
            set { _logformat = value; }
        }
        private string _logTable = String.Empty;

        #region 公有方法
        [Obsolete("该方法为保持原有接口,而预留.如设置LogFormat属性为Database,该方法会错误,请使用WriteLog(string content)的重载")]
        public void WriteLog(string path, string content)
        {
            if (String.IsNullOrEmpty(path)) throw new ArgumentNullException("日志存放路径不能为空");
            if (content == String.Empty) return;
            System.IO.FileStream fs = null;
            if (!System.IO.File.Exists(path))
            {
                fs = new System.IO.FileStream(path, System.IO.FileMode.Create);
                fs.Close();
                fs.Dispose();
                fs = null;
            }

            bool needZip = false;
            needZip = CheckNeedCompressFile(path);
            if (needZip)
                CompressLogFile(path);

            if (_logformat == LogFormat.XML)
                WriteXMLLog(content);
            else if (_logformat == LogFormat.Text)
                WriteTextLog(path,content);
        }

        public void WriteLog(string content)
        {
            if (this._logformat == LogFormat.Text || this._logformat == LogFormat.XML)
                WriteLog(_logPath, content);
        }
        #endregion

        private bool CheckNeedCompressFile(string path)
        {
            if (_logstoremode != LogStoreMode.Compression) return false;
            if (_logsavemode == LogSaveMode.Limit)
            {
                StreamReader sr = new StreamReader(path);
                string all = sr.ReadToEnd();
                sr.Close();
                sr.Dispose();
                if (all.Length >= _filesizelimit)
                {
                    return true;
                }
            }
            return false;
        }

        private void CompressLogFile(string path)
        {
            string zipFileName = Guid.NewGuid().ToString() + ".zip"; //Path.GetFileNameWithoutExtension(_logPath) + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
            string zipfilePath = Path.GetDirectoryName(path);
            List<string> files = new List<string>();
            files.Add(path);

            using (Zipper zipper = new Zipper())
            {
                zipper.ZipFiles(files, zipfilePath + @"\" + zipFileName);
                File.Delete(path);
                System.IO.FileStream fs = null;
                if (!System.IO.File.Exists(path))
                {
                    fs = new System.IO.FileStream(path, System.IO.FileMode.Create);
                    fs.Close();
                    fs.Dispose();
                    fs = null;
                }
            }
            return;
        }

        private void WriteXMLLog(string content)
        {
            return;
        }

        /// <summary>
        /// 写入纯文本的日志文件
        /// </summary>
        private void WriteTextLog(string content)
        {
            WriteTextLog(_logPath, content);
        }

        private void WriteTextLog(string path, string content)
        {
            string host = Dns.GetHostName();
            try
            {
                StreamWriter sw = new StreamWriter(path, true, Encoding.Default);
                sw.WriteLine(String.Format("Time:[{0}],Host:[{1}],Message[{2}]", DateTime.Now.ToString(), host, content));
                sw.Flush();
                sw.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    /// <summary>
    /// 日志工厂
    /// </summary>
    public sealed class LoggerFactory
    {
        /// <summary>
        /// 私有构造函数,防止实例化
        /// </summary>
        private LoggerFactory()
        {
        }

        /// <summary>
        /// 建立新日志
        /// </summary>
        /// <param name="logFormat"></param>
        /// <returns></returns>
        public static ILogger CreateLogger(LogFormat logFormat)
        {
            switch (logFormat)
            {
                case LogFormat.Text:
                    return TextLogger.CreateLogger();
                case LogFormat.Database:
                    return DatabaseLogger.CreateLogger();
                case LogFormat.XML:
                    throw new NotImplementedException("XML格式日志尚未实现");
                default:
                    throw new ArgumentException("错误的日志格式");
            }
        }
    }

    /// <summary>
    /// 日志设置接口
    /// </summary>
    public interface ILoggerSetting
    {
        LogSaveMode SaveMode { get; set; }
    }

    /// <summary>
    /// 记录数据库日志的设置
    /// </summary>
    public class DatabaseLoggerSetting : ILoggerSetting
    {
        private string _connectionString = String.Empty;
        /// <summary>
        /// 连接字符串
        /// </summary>
        public string ConnectionString
        {
            get { return _connectionString; }
            set { _connectionString = value; }
        }
        private string _logTableName = String.Empty;
        /// <summary>
        /// 日志表名
        /// </summary>
        public string LogTableName
        {
            get { return _logTableName; }
            set { _logTableName = value; }
        }
        private string _createTableString = String.Empty;
        /// <summary>
        /// 建表语句
        /// </summary>
        public string CreateTableString
        {
            get { return _createTableString; }
            set { _createTableString = value; }
        }
        private string _insertLogString = String.Empty;
        /// <summary>
        /// 日志记录插入语句
        /// </summary>
        public string InsertLogString
        {
            get { return _insertLogString; }
            set { _insertLogString = value; }
        }
        private LogSaveMode _saveMode = LogSaveMode.Single;
        /// <summary>
        /// 日志信息存在模式
        /// </summary>
        public LogSaveMode SaveMode
        {
            get { return _saveMode; }
            set { _saveMode = value; }
        }
    }

    /// <summary>
    /// 记录文本日志的设置
    /// </summary>
    public class TextLoggerSetting : ILoggerSetting
    {
        private long _filesizelimit = (20 * 1024 * 1024);
        /// <summary>
        /// 日志限制的大小(默认为20M)
        /// </summary>
        public long FileSizeLimit
        {
            get { return _filesizelimit; }
            set { _filesizelimit = value; }
        }
        private string _logPath = String.Empty;
        /// <summary>
        /// 日志保存路径
        /// </summary>
        public string LogPath
        {
            get { return _logPath; }
            set { _logPath = value; }
        }
        private LogStoreMode _logstoremode = LogStoreMode.Compression;
        /// <summary>
        /// 日志的存在模式
        /// </summary>
        public LogStoreMode LogStoreMode
        {
            get { return _logstoremode; }
            set { _logstoremode = value; }
        }

        private LogSaveMode _logsavemode = LogSaveMode.Limit;
        public LogSaveMode SaveMode
        {
            get { return _logsavemode; }
            set { _logsavemode = value; }
        }
    }

    /// <summary>
    /// 文本日志
    /// </summary>
    public class TextLogger : ILogger
    {
        #region 单件
        private static TextLogger _logger = null;
        private TextLogger()
        {
        }
        public static TextLogger CreateLogger()
        {
            if (_logger == null)
                _logger = new TextLogger();
            return _logger;
        }
        #endregion

        protected TextLoggerSetting _setting = null;

        /// <summary>
        /// 数据库日志配置
        /// </summary>
        public ILoggerSetting Settings
        {
            private get { return this._setting; }
            set { this._setting = (TextLoggerSetting)value; }
        }

        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="content">日志内容</param>
        public void WriteLog(LogType type, string content)
        {
            throw new NotImplementedException();
        }

        public void WriteLog(LogType type, params string[] contents)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 读取日志配置
        /// </summary>
        /// <param name="settingNode">日志配置节</param>
        /// <returns>配置类</returns>
        public void LoadLogSetting(XmlNode settingNode)
        {
            if (settingNode == null) throw new ArgumentNullException("日志配置节点为空");
            if (settingNode["LogPath"] != null)
                this._setting.LogPath = settingNode["LogPath"].InnerText;

            string tmp = String.Empty;
            if (settingNode["SaveMode"] != null)
            {
                tmp = settingNode["SaveMode"].InnerText.ToLower();
                if (tmp == "cycle")
                    this._setting.SaveMode = LogSaveMode.Cycle;
                else if (tmp == "limit")
                    this._setting.SaveMode = LogSaveMode.Limit;
                else if (tmp == "single")
                    this._setting.SaveMode = LogSaveMode.Single;
                else
                    this._setting.SaveMode = LogSaveMode.Single;
            }
        }

        /// <summary>
        /// 读取日志配置
        /// </summary>
        /// <param name="configFilePath">配置文件路径</param>
        /// <param name="xPath">配置节的XPath</param>
        /// <returns>配置类</returns>
        public void LoadLogSetting(string configFilePath, string xPath)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 建立日志文件夹
        /// </summary>
        protected void BuildLogFolder()
        {
            if (String.IsNullOrEmpty(this._setting.LogPath)) throw new ArgumentNullException("日志的目录不能为空");
            //如果不存在指定的目录
            if (!Directory.Exists(this._setting.LogPath))
                Directory.CreateDirectory(this._setting.LogPath);
        }
    }

    /// <summary>
    /// 数据库日志
    /// </summary>
    public class DatabaseLogger : ILogger
    {
        #region 单件
        private static DatabaseLogger _logger = null;
        private static SqlConnection _connection = new SqlConnection();

        private DatabaseLogger()
        {
            if (String.IsNullOrEmpty(this._setting.ConnectionString))
                throw new ArgumentNullException("日志数据库的连接字符串不能为空");
            _connection.ConnectionString = this._setting.ConnectionString;
        }
        public static DatabaseLogger CreateLogger()
        {
            if (_logger == null)
                _logger = new DatabaseLogger();
            return _logger;
        }
        #endregion

        protected DatabaseLoggerSetting _setting = null;
        private static object _Sync = new object();
        /// <summary>
        /// 数据库日志配置
        /// </summary>
        public ILoggerSetting Settings
        {
            private get { return this._setting; }
            set { this._setting = (DatabaseLoggerSetting)value; }
        }

        /// <summary>
        /// 写入日志[该方法适合默认的日志表格式]
        /// </summary>
        /// <param name="content">日志内容</param>
        public void WriteLog(LogType logtype, string content)
        {
            if (String.IsNullOrEmpty(content.Trim()))
                throw new ArgumentNullException("日志内容不能为空");
            SqlCommand command = new SqlCommand();
            lock (_Sync)
            {
                try
                {
                    command.Connection = _connection;
                    if (!String.IsNullOrEmpty(this._setting.LogTableName))
                        command.CommandText = String.Format("Insert Into {0}(LogType,HostName,LogMessage,LogDate) Values({1},{2},{3},{4})",
                            this._setting.LogTableName, logtype, Dns.GetHostName(), content, DateTime.Now);
                    else
                        command.CommandText = String.Format("Insert Into OperateLog(LogType,HostName,LogMessage,LogDate) Values({0},{1},{2},{3})",
                            logtype, Dns.GetHostName(), content, DateTime.Now);
                    command.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.EventLog.WriteEntry("CommonLogMoudle", ex.Message);
                }
                finally
                {
                    command.Dispose();
                    _connection.Close();
                }
            }
        }

        /// <summary>
        /// 写入日志[该方法时候自定义的日志写入格式]
        /// </summary>
        /// <param name="contents">需要写入日志的内容</param>
        public void WriteLog(LogType logtype, params string[] contents)
        {
            if (contents.Length == 0)
                throw new ArgumentNullException("日志内容不能为空");
            SqlCommand command = new SqlCommand();
            lock (_Sync)
            {
                try
                {
                    command.Connection = _connection;
                    if (!String.IsNullOrEmpty(this._setting.LogTableName))
                        command.CommandText = String.Format(this._setting.InsertLogString, this._setting.LogTableName, logtype, contents);
                    else
                        command.CommandText = String.Format(this._setting.InsertLogString, logtype, contents);
                    command.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.EventLog.WriteEntry("CommonLogMoudle", ex.Message);
                }
                finally
                {
                    command.Dispose();
                    _connection.Close();
                }
            }
        }

        /// <summary>
        /// 读取日志配置
        /// </summary>
        /// <param name="settingNode">日志配置节</param>
        /// <returns>配置类</returns>
        public void LoadLogSetting(XmlNode settingNode)
        {
            if (settingNode == null)
                throw new ArgumentNullException("配置节点为空");
            if (settingNode["LogDbConnString"] == null)
                throw new XmlException("日志配置节点不能为空");
            this._setting.ConnectionString = settingNode["LogDbConnString"].InnerText;
            if (settingNode["LogTableName"] != null)
                this._setting.LogTableName = settingNode["LogTableName"].InnerText;
            if (settingNode["CreateTableString"] != null)
                this._setting.LogTableName = settingNode["CreateTableString"].InnerText;
            if (settingNode["InsertLogString"] != null)
                this._setting.LogTableName = settingNode["InsertLogString"].InnerText;
        }

        /// <summary>
        /// 读取日志配置
        /// </summary>
        /// <param name="configFilePath">配置文件路径</param>
        /// <param name="xPath">配置节的XPath</param>
        /// <returns>配置类</returns>
        public void LoadLogSetting(string configFilePath, string xPath)
        {
            if (!File.Exists(configFilePath))
                throw new FileNotFoundException("日志文件未找到");
            if (String.IsNullOrEmpty(xPath))
                throw new ArgumentNullException("XPath路径不能为空");
            XmlDocument doc = new XmlDocument();
            doc.Load(configFilePath);
            XmlNode node = doc.SelectSingleNode(xPath);
            if (node == null)
                throw new XmlException("指定的配置节点不存在");
            LoadLogSetting(node);
        }

        /// <summary>
        /// 建立存放日志的表
        /// </summary>
        protected void BuildLogTable()
        {
            if (String.IsNullOrEmpty(this._setting.ConnectionString))
                throw new ArgumentNullException("连接字符串不能为空");

            SqlCommand command = new SqlCommand();
            if (_connection.State != ConnectionState.Open)
                _connection.Open();
            command.Connection = _connection;
            if (String.IsNullOrEmpty(this._setting.LogTableName))
                command.CommandText = "Select Count(1) From sys.tables Where [name]='OperateLog'";
            else
                command.CommandText = "Select Count(1) From sys.tables Where [name]='" + this._setting.LogTableName + "'";
            object tmpCount = command.ExecuteScalar();
            command.Connection.Close();
            if (Convert.ToInt32(tmpCount) == 0)
            {
                if (String.IsNullOrEmpty(this._setting.CreateTableString))
                {
                    if(String.IsNullOrEmpty(this._setting.LogTableName))
                        command.CommandText = "CREATE TABLE [dbo].[OperateLog]([LogID] [int] IDENTITY(1,1) NOT NULL,[LogType] [int] NOT NULL," +
                        "[HostName] [varchar](50) NULL,[LogMessage] [varchar](200) NULL,[LogDate] [datetime] NULL" +
                        "CONSTRAINT [PK_CountryIP] PRIMARY KEY NONCLUSTERED([LogID] ASC)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF," +
                        "IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]" +
                        ")ON [PRIMARY]";
                    else
                        command.CommandText = "CREATE TABLE " + this._setting.LogTableName + "([LogID] [int] IDENTITY(1,1) NOT NULL,[LogType] [int] NOT NULL," +
                        "[HostName] [varchar](50) NULL,[LogMessage] [varchar](200) NULL,[LogDate] [datetime] NULL" +
                        "CONSTRAINT [PK_CountryIP] PRIMARY KEY NONCLUSTERED([LogID] ASC)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF," +
                        "IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]" +
                        ")ON [PRIMARY]";
                }
                else
                    command.CommandText = String.Format(this._setting.CreateTableString, this._setting.LogTableName);
                if (_connection.State != ConnectionState.Open)
                    _connection.Open();
                command.ExecuteNonQuery();
            }
        }
    }

    /// <summary>
    /// 日志接口
    /// </summary>
    public interface ILogger
    {
        ILoggerSetting Settings { set; }
        void WriteLog(LogType logtype, string content);
        void WriteLog(LogType logtype, params string[] contents);
        void LoadLogSetting(XmlNode settingNode);
        void LoadLogSetting(string configFilePath, string xPath);
    }
}
